﻿using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace CommonLib
{
	public class QuadTreeNode
	{
		public static int Unique = 0;

		protected int id;
		protected int deepth;
		protected float rootMinSize;
		protected QuadTreeNode root;
		protected Dictionary<QuadTreeNodeType, QuadTreeNode> dicChildNode;
		protected List<QuadTreeNode> lstChildNode;
		protected List<QuadTreeReference> lstReference;

		public QuadTreeNodeType QuadType { get; protected set; } = QuadTreeNodeType.None;
		public Bounds QuadBounds { get; protected set; }
		public int Count
		{
			get
			{
				if (null == lstChildNode)
					return lstReference.Count;
				else
				{
					int sum = 0;
					for (int i = 0; i < lstChildNode.Count; i++)
					{
						sum += lstChildNode[i].Count;
					}
					sum += lstReference.Count;
					return sum;

				}
			}
		}
		public int InstanceId => id;
		public int NodeDeepth => deepth;
		public QuadTreeNode Root => root;
		public bool IsLeaf => CanCreateChild();


		public void AddNodeReference(QuadTreeReference reference)
		{
			lstReference.Add(reference);
			reference.UpdateSpaceNode(this);
		}
		public void AddNodeReference(GameObject go)
		{
			var rf = go.AddComponent<QuadTreeReference>();
			AddNodeReference(rf);
		}
		public void RemoveNodeReference(QuadTreeReference reference)
		{
			lstReference.Remove(reference);
			GameObject.Destroy(reference);
			//Debug.Log(Count);
		}
		public void RemoveNodeReference(GameObject go)
		{
			var rf = go.GetComponent<QuadTreeReference>();
			
			if (rf != null)
				RemoveNodeReference(rf);
		}

		public QuadTreeNode(Vector3 center, Vector3 size, QuadTreeNodeType type, float minSize, int deepth , QuadTreeNode root)
		{
			this.id = QuadTreeNode.Unique++;
			QuadBounds = new Bounds(center, size);
			lstReference = new List<QuadTreeReference>();
			QuadType = type;
			this.deepth = deepth;
			this.rootMinSize = minSize;
			this.root = root;
			QuadTreeNodeHolder.Instance.AddNode(this);
		}

		/// <summary>
		/// 创建子节点
		/// </summary>
		virtual protected void CreateChildNode()
		{
			if (CanCreateChild())
			{
				dicChildNode = new Dictionary<QuadTreeNodeType, QuadTreeNode>();
				lstChildNode = new List<QuadTreeNode>();

				Vector3 newSize = QuadBounds.size * 0.5f;
				float offsetX = QuadBounds.size.x * 0.25f;
				float offsetY = QuadBounds.size.y * 0.25f;

				//UR
				QuadTreeNode urNode = new QuadTreeNode((QuadBounds.center + new Vector3(offsetX, offsetY, 0)), newSize, QuadTreeNodeType.UR, rootMinSize, deepth + 1, this);
				dicChildNode.Add(QuadTreeNodeType.UR, urNode);
				lstChildNode.Add(urNode);
				QuadTreeNodeHolder.Instance.AddNode(urNode);
				//UL
				QuadTreeNode ulNode = new QuadTreeNode((QuadBounds.center + new Vector3(-offsetX, offsetY, 0)), newSize, QuadTreeNodeType.UL, rootMinSize, deepth + 1, this);
				dicChildNode.Add(QuadTreeNodeType.UL, ulNode);
				lstChildNode.Add(ulNode);
				QuadTreeNodeHolder.Instance.AddNode(ulNode);
				//BL
				QuadTreeNode blNode = new QuadTreeNode((QuadBounds.center + new Vector3(-offsetX, -offsetY, 0)), newSize, QuadTreeNodeType.BL, rootMinSize, deepth + 1, this);
				dicChildNode.Add(QuadTreeNodeType.BL, blNode);
				lstChildNode.Add(blNode);
				QuadTreeNodeHolder.Instance.AddNode(blNode);
				//BR
				QuadTreeNode brNode = new QuadTreeNode((QuadBounds.center + new Vector3(offsetX, -offsetY, 0)), newSize, QuadTreeNodeType.BR, rootMinSize, deepth + 1, this);
				dicChildNode.Add(QuadTreeNodeType.BR, brNode);
				lstChildNode.Add(brNode);
				QuadTreeNodeHolder.Instance.AddNode(brNode);
			}
		}

		/// <summary>
		/// 对一个带个有bounds游戏物体进行检测
		/// </summary>
		/// <param name="go"></param>
		/// <param name="bounds"></param>
		/// <returns></returns>
		virtual internal bool CheckObject(GameObject go, Bounds bounds)
		{
			//Vector3 cloest = QuadBounds.ClosestPoint(bounds.center);
			if (
				QuadBounds.min.x <= bounds.min.x &&
				QuadBounds.max.x >= bounds.max.x &&

				QuadBounds.min.y <= bounds.min.y &&
				QuadBounds.max.y >= bounds.max.y
				)
			{
				if(null == lstChildNode)
					CreateChildNode();
				bool childContaion = false;
				if (lstChildNode != null)
				{
					foreach (var child in lstChildNode)
					{
						childContaion = childContaion || child.CheckObject(go, bounds);
					}
				}
				if (childContaion == false)
				{
					QuadTreeReference refer = go.GetComponent<QuadTreeReference>();
					if ( null == refer)
					{
						refer = go.AddComponent<QuadTreeReference>();
					}
					//refer.UpdateSpaceNode(this);
					AddNodeReference(refer);
				}
				return true;
			}
			else
				return false;
		}

		/// <summary>
		/// 对一个游戏物体进行检测位置
		/// </summary>
		/// <param name="gameObject"></param>
		/// <returns></returns>
		virtual internal bool CheckObject(GameObject gameObject)
		{

			if (QuadBounds.Contains(gameObject.transform.position))
			{
				if (null == lstChildNode)
					CreateChildNode();
				bool childContaion = false;
				if (lstChildNode != null)
				{
					foreach (var child in lstChildNode)
					{
						childContaion = childContaion || child.CheckObject(gameObject);
					}
				}
				if (childContaion == false)
				{
					QuadTreeReference refer = gameObject.GetComponent<QuadTreeReference>();
					if (null == refer)
					{
						refer = gameObject.AddComponent<QuadTreeReference>();
					}
					//refer.UpdateSpaceNode(this);
					AddNodeReference(refer);
				}
				return true;
			}
			else
				return false;
		}

		/// <summary>
		/// 对于空间一个位置进行位置检测
		/// </summary>
		/// <param name="position"></param>
		/// <returns></returns>
		virtual internal int CheckObject(Vector3 position)
		{
			if (QuadBounds.Contains(position))
			{
				if (null == lstChildNode)
					CreateChildNode();
				int instanceId = this.InstanceId;

				if (lstChildNode != null)
				{
					foreach (var child in lstChildNode)
					{
						//childContaion = childContaion || child.CheckObject(position) >= 0;
						int temp = child.CheckObject(position);
						if (temp > 0) instanceId = temp;
					}
				}
				return instanceId;
			}
			else
				return -1;
		}


		virtual protected bool CanCreateChild()
		{
			if(QuadBounds.size.x >= 2 * rootMinSize && QuadBounds.size.y >= 2 * rootMinSize)
				return true;
			else
				return false;
		}

		virtual public QuadTreeNode GetParentByDeepth(int target)
		{
			QuadTreeNode node = Root;
			while ( null != Root)
			{
				if (node.NodeDeepth == target)
					return node;
				else
					node = node.Root;
			}
			return null;
		}

		#region InScenenArea  DrawCubeBound
		public void DrawCude()
		{
			Gizmos.color = Color.white;
			if (this.lstReference.Count != 0)
				Gizmos.color = Color.red;
			//Gizmos.color = new Color(NodeDeepth * 100, NodeDeepth * 10, NodeDeepth * 10);
			if (QuadType == QuadTreeNodeType.None)
				Gizmos.DrawWireCube(QuadBounds.center, QuadBounds.size);
			else
				Gizmos.DrawWireCube(QuadBounds.center, QuadBounds.size - new Vector3(0.05f * deepth, 0.05f * deepth, 0));
			if (null == lstChildNode) return;
			for (int i = 0; i < lstChildNode.Count; i++)
			{
				lstChildNode[i].DrawCude();
			}
			Gizmos.color = Color.white;
		}
		#endregion
	}


	public enum QuadTreeNodeType
	{
		None = 0, //根节点
		UR = 1, //右上第一象限
		UL = 2, //左上第二象限
		BL = 3, //坐下第三象限
		BR = 4, //右下第四象限
	}
}